package net.cathackers.devel.scmp.chat.impl.groupchat;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;

import net.cathackers.devel.scmp.auth.impl.jabber.JabberUserID;
import net.cathackers.devel.scmp.auth.interfaces.UserID;
import net.cathackers.devel.scmp.chat.exceptions.ConflictingNicknameException;
import net.cathackers.devel.scmp.chat.interfaces.ChatManager;
import net.cathackers.devel.scmp.chat.interfaces.GroupChatManager;
import net.cathackers.devel.scmp.factories.SCMPFactory;
import net.cathackers.devel.scmp.xmpp.MessagePacket;
import net.cathackers.devel.scmp.xmpp.Packet;
import net.cathackers.devel.scmp.xmpp.PresencePacket;
import net.cathackers.devel.scmp.xmpp.StanzaErrors;
import net.cathackers.devel.scmp.xmpp.MessagePacket.MessageType;

public class GroupChatManagerImpl implements GroupChatManager {
    private Logger logger_;
    private Hashtable<String, GroupChat> groups_;
    private String serverName_;
    private ChatManager chatManager_;

    public GroupChatManagerImpl() {
	serverName_ = SCMPFactory.getConfiguration().getGroupChatServerDomain();
	groups_ = new Hashtable<String, GroupChat>();
	logger_ = SCMPFactory.getLogger(this);
	chatManager_ = SCMPFactory.getChatManager();
	logger_.debug("Group Chat server started on domain " + serverName_);
    }

    @Override
    public GroupChat createGroupchat(String groupName) {
	GroupChat ret = null;
	if (groupName != null) {
	    if (!groups_.containsKey(groupName)) {
		ret = new GroupChat(new JabberUserID(groupName + "@"
			+ serverName_));
		groups_.put(groupName, ret);
		logger_.debug("New GroupChat with the name " + groupName
			+ " is created");
	    } else {
		logger_.error("the group " + groupName + " alreay exist ");
	    }
	}
	return ret;
    }

    @Override
    public GroupChat getGroupChat(String name) {
	GroupChat ret = null;
	if (name != null) {
	    if (groups_.containsKey(name)) {
		logger_.debug("the group  " + name + " already exist");
		ret = groups_.get(name);
	    } else {
		logger_.error("the group " + name + " not found ");
		ret = this.createGroupchat(name);
	    }
	}
	return ret;
    }

    @Override
    public void handleMessage(MessagePacket message) {
	UserID to = message.getTo();
	UserID from = message.getFrom();
	GroupChat group = getGroupChat(to.getUsername());
	UserID sender = new JabberUserID(group.getGroupID().getCanonicalName()
		+ "/" + group.getNickname(from));
	message.setFrom(sender);
	if (to.getResource() != null) {
	    logger_.debug("the message was addressed to  " + to.getResource());
	    message.setTo(group.getUserID(to.getResource()));
	    chatManager_.deliverToUser(message);
	} else {
	    logger_
		    .debug("the message was addressed to all members in this group");
	    this.deliverToAll(group, message);
	}

    }

    private void deliverToAll(GroupChat group, Packet packet) {
	List<UserID> users = group.getAllUsers();
	for (Iterator<UserID> i = users.iterator(); i.hasNext();) {
	    Packet copyOfPacket = new Packet(packet.getDocument());
	    copyOfPacket.setTo(i.next());
	    chatManager_.deliverToUser(copyOfPacket);
	}

    }

    @Override
    public void handlePresence(PresencePacket presence) {
	UserID to = presence.getTo();
	if (!isGroupChatRequest(presence)) {
	    return;
	}
	GroupChat group = getGroupChat(to.getUsername());
	String nickName = to.getResource();
	try {
	    if (group.isExist(nickName, presence.getFrom())) {
		logger_.debug("The user " + nickName + " exists ");

		updatePresence(group, presence);
	    } else {
		joinGroupChat(group, presence);
	    }
	} catch (ConflictingNicknameException e) {
	    logger_.error(e.getMessage());
	    this.sendConflictingNicknameError(presence);
	}

    }

    private void sendConflictingNicknameError(PresencePacket presence) {
	presence.switchDirection();
	StanzaErrors error = new StanzaErrors(StanzaErrors.Condition.conflict);
	presence.setError(error);
	chatManager_.deliverToUser(presence);

    }

    private void updatePresence(GroupChat group, PresencePacket presence) {
	UserID from = presence.getFrom();
	UserID sender = new JabberUserID(group.getGroupID().getCanonicalName()
		+ "/" + group.getNickname(from));
	presence.setFrom(sender);
	this.deliverToAll(group, presence);
	group.updatePresence(from, presence);
	if (presence.getPresenceType() == null) {
	    return;
	}
	if (presence.getPresenceType().equals("unavailable")) {
	    this.leaveGroupChat(group, from);
	}
    }

    @Override
    public boolean isGroupChatRequest(Packet packet) {
	boolean ret = false;
	UserID to = packet.getTo();
	if (to != null) {
	  //  ret = to.getDomain().equalsIgnoreCase(serverName_);
	    ret = to.getUsername().endsWith(".group") && to.getDomain().matches(serverName_);
	    
	    logger_.debug("Is this request of GroupChat type ? " + ret);
	}
	return ret;
    }

    @Override
    public void joinGroupChat(GroupChat group, PresencePacket packet) {
	UserID to = packet.getTo();
	UserID from = packet.getFrom();
	group.addUser(to.getResource(), from);
	updatePresence(group, packet);
	PresencePacket[] presences = group.getAllUserPresences();
	logger_
		.debug("send the presence of all Group users to "
			+ from.getID());
	for (PresencePacket presence : presences) {
	    presence.setTo(from);
	    chatManager_.deliverToUser(presence);
	}
	String mesg = to.getResource() + " has joined the group";
	logger_.debug(mesg);
	this.deliverToAll(group, createServerMessage(group, mesg));
    }

    private MessagePacket createServerMessage(GroupChat group, String mesg) {
	Document doc = DocumentHelper.createDocument();
	doc.addElement("message");
	MessagePacket serverMessage = new MessagePacket(doc);
	serverMessage.setMessageType(MessageType.groupchat);
	serverMessage.setFrom(group.getGroupID());
	serverMessage.setBody(mesg);
	return serverMessage;
    }

    @Override
    public void leaveGroupChat(GroupChat group, UserID userID) {
	group.removeUser(userID);
	if (group.isEmpty()) {
	    this.removeGroupChat(group);
	}
	logger_.debug(userID + "has left the group");
	String mesg = group.getNickname(userID) + " has left";
	this.deliverToAll(group, createServerMessage(group, mesg));

    }

    @Override
    public boolean removeGroupChat(GroupChat group) {
	boolean ret = false;
	if (group != null) {
	    if (group.isEmpty()) {
		groups_.remove(group);
		ret = true;
		logger_.debug("this group " + group.getGroupID().getID()
			+ " has removed");
	    }
	}
	return ret;
    }

}
